using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using FibMatrix.IO;
using FM_Mono;
using UnityEngine;

#if UNITY_EDITOR
[FM_Mono.IgnoreGen]
public class EditorScriptConfig : IScriptConfig
{
#if UNITY_EDITOR_OSX
    private const string FM_MONO_PLUGIN_NAME = "fm_mono_glue.bundle";
#else 
    private const string FM_MONO_PLUGIN_NAME = "fm_mono_glue";
#endif

    private string _libDir;
    public string libraryDirectory
    {
        get
        {
            return _libDir;
        }
    }

    private string _configDir;
    public string configDirectory
    {
        get
        {
            return _configDir;
        }
    }

    public string overrideConfig
    {
        get
        {
            return "";
        }
    }

    public FM_MonoEnv.ImageLoadCallback imageLoadFunc
    {
        get
        {
            return LoadImage;
        }
    }

    private string _commonAssemblyDir;
    private string _userAssemblyDir;
    private HashSet<string> _loadedAssemblySet = new HashSet<string>();

    private bool _configured = false;
    
    public EditorScriptConfig(string userAssemblyDir)
    {
        Configure(userAssemblyDir);
    }

    private void Configure(string userAssemblyDir)
    {
        _commonAssemblyDir = Path.GetFullPath(Path.Combine("Assets", "FM_Mono", "Plugins"));
        _userAssemblyDir = userAssemblyDir;
        // string platformDir = null;
        // string platformBclDir = "desktop";
        
        // #if UNITY_EDITOR_OSX
        //     platformDir = "osx_x86_64";
        // #elif UNITY_EDITOR_WIN
        //     platformDir = Environment.Is64BitProcess ? "win_x86_64" : "win_x86";
        //     bclDir = "desktop-win32"
        // #endif

        // if (platformDir == null)
        // {
        //     throw new NotSupportedException("Unsupport Editor Platform");
        // }

        // string pathPrefix = Path.GetFullPath(Path.Combine(monoRuntimeDir, "bcl", platformBclDir));
        
        foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            _loadedAssemblySet.Add(assembly.GetName().Name);
        }

        string[] guids = UnityEditor.AssetDatabase.FindAssets(FM_MONO_PLUGIN_NAME);
        if (guids == null || guids.Length == 0)
        {
            Debug.Log("Can't find plugin in project");
            return;
        }
        string pluginPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[0]);
        UnityEditor.PluginImporter pluginImporter = UnityEditor.AssetImporter.GetAtPath(pluginPath) as UnityEditor.PluginImporter;
        if (pluginImporter == null || !pluginImporter.isNativePlugin)
        {
            return;
        }

        _libDir = "";//Path.Combine(pathPrefix, "lib");
        _configDir = "";//Path.Combine(pathPrefix, "etc");
        _configured = true;
    }

    private static string ReplaceExt(string path, string newExt)
    {
        return Path.GetFileNameWithoutExtension(path) + newExt;
    }

    private IntPtr LoadImage(string aname, string culture, out uint size)
    {
        size = 0;
        if (_configured)
        {
            string oriAname = aname;
            if (oriAname.EndsWith(".dll"))
            {
                oriAname = oriAname.Substring(0, oriAname.Length - 4);
            }
            Debug.Log("Load Assembly " + aname);
            if (!aname.EndsWith(".dll"))
            {
                aname += ".dll";
            }
            string filePath;
            if (_loadedAssemblySet.Contains(oriAname) || _loadedAssemblySet.Contains(aname))
            {
                return IntPtr.Zero;//File.ReadAllBytes(filePath);
            }
            string prefix = _userAssemblyDir;
            if (aname == "FM_MonoLib.dll")
            {
                prefix = _commonAssemblyDir;
            }
            filePath = Path.Combine(prefix, aname);
            string symbolPath = Path.Combine(prefix, ReplaceExt(aname, ".pdb"));
            if (StorageManager.Exists(filePath))
            {
                byte[] result = StorageManager.ReadAllBytes(filePath);
                byte[] symbolResult = StorageManager.Exists(symbolPath) ? StorageManager.ReadAllBytes(symbolPath) : null;
                Assembly assembly = AppDomain.CurrentDomain.Load(result, symbolResult);
                if (assembly != null)
                {
                    FM_Mono.FM_MonoDLL.LoadAssemblyWithImageBinary(oriAname, null, 0, 1);
                    Debug.Log("Load Assembly " + assembly.GetName().Name + " success");
                    foreach(var rassemblyNameRef in assembly.GetReferencedAssemblies())
                    {
                        string raname = rassemblyNameRef.Name;
                        if (!_loadedAssemblySet.Contains(raname))
                        {
                            string rfilePath = Path.Combine(prefix, raname + ".dll");
                            string rSymbolPath = Path.Combine(prefix, raname + ".pdb");
                            if (StorageManager.Exists(rfilePath))
                            {
                                byte[] rResult = StorageManager.ReadAllBytes(rfilePath);
                                byte[] rSymbolResult = StorageManager.Exists(rSymbolPath) ? StorageManager.ReadAllBytes(rSymbolPath) : null;
                                Assembly rAssembly = AppDomain.CurrentDomain.Load(rResult, rSymbolResult);
                                if (rAssembly != null)
                                {
                                    FM_Mono.FM_MonoDLL.LoadAssemblyWithImageBinary(raname, null, 0, 1);
                                    Debug.Log("Load Assembly " + rAssembly.GetName().Name + " success");
                                }
                            }
                        }
                    }
                    return IntPtr.Zero;
                }
            }
            Debug.Log("Load Assembly " + filePath + " fail");
        }
        return IntPtr.Zero;
    }
}

#endif